﻿using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
using System.Web.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using WebMatrix.Data;

[TestClass]
public class FreemiumHelperTests
{
    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void ShouldThrowExceptionIfDbNameIsNotSet()
    {
        Freemium.DatabaseName = null;
    }

    [TestMethod]
    public void ShouldInitializeDB()
    {
        Assert.IsFalse(File.Exists("freemiumdb.sdf"));

        SqlCeConnectionStringBuilder connBuilder = new SqlCeConnectionStringBuilder();
        connBuilder["Data Source"] = string.Format("freemiumdb.sdf");

        using (SqlCeConnection connection = new SqlCeConnection(connBuilder.ConnectionString))
        {
            using (SqlCeEngine engine = new SqlCeEngine(connBuilder.ConnectionString))
            {
                engine.CreateDatabase();
            }
        }

        Freemium.Initialize("freemiumdb");

        using (SqlCeConnection connection = new SqlCeConnection(connBuilder.ConnectionString))
        {
            connection.Open();
            var tables = connection.GetSchema("Tables");

            Assert.AreEqual(1, tables.Select("table_name = 'freemium_Feature'").Count());
            Assert.AreEqual(1, tables.Select("table_name = 'freemium_StockKeepingUnit'").Count());
            Assert.AreEqual(1, tables.Select("table_name = 'freemium_FeatureStockKeepingUnit'").Count());
            Assert.AreEqual(1, tables.Select("table_name = 'freemium_StockKeepingUnitImage'").Count());
        }
    }

    [TestMethod]
    public void ShouldInsertDefaultValuesToDB()
    {
        Assert.IsFalse(File.Exists("defaultfreemiumdb.sdf"));

        SqlCeConnectionStringBuilder connBuilder = new SqlCeConnectionStringBuilder();
        connBuilder["Data Source"] = string.Format("defaultfreemiumdb.sdf");

        using (SqlCeConnection connection = new SqlCeConnection(connBuilder.ConnectionString))
        {
            using (SqlCeEngine engine = new SqlCeEngine(connBuilder.ConnectionString))
            {
                engine.CreateDatabase();
            }
        }

        Freemium.Initialize("defaultfreemiumdb", createBaseDataSet:true);

        using (Database db = Database.Open("defaultfreemiumdb"))
        {            
            var features = db.Query("SELECT * FROM freemium_Feature");
            var skus = db.Query("SELECT * FROM freemium_StockKeepingUnit");
            var featureSkus = db.Query("SELECT f.Name FeatureName, s.Name SkuName FROM freemium_FeatureStockKeepingUnit fs, freemium_Feature f, freemium_StockKeepingUnit s WHERE fs.FeatureId = f.Id AND fs.StockKeepingUnitId = s.Id");

            Assert.AreEqual(3, features.Count());
            Assert.AreEqual("Feature 1", features.ElementAt(0).Name);
            Assert.AreEqual("Feature 2", features.ElementAt(1).Name);
            Assert.AreEqual("Feature 3", features.ElementAt(2).Name);

            Assert.AreEqual(3, skus.Count());
            Assert.AreEqual("Gold", skus.ElementAt(0).Name);
            Assert.AreEqual("Silver", skus.ElementAt(1).Name);
            Assert.AreEqual("Free", skus.ElementAt(2).Name);

            Assert.AreEqual(6, featureSkus.Count());
            Assert.AreEqual("Feature 1", featureSkus.ElementAt(0).FeatureName);
            Assert.AreEqual("Gold", featureSkus.ElementAt(0).SkuName);
            Assert.AreEqual("Feature 2", featureSkus.ElementAt(1).FeatureName);
            Assert.AreEqual("Gold", featureSkus.ElementAt(1).SkuName);
            Assert.AreEqual("Feature 3", featureSkus.ElementAt(2).FeatureName);
            Assert.AreEqual("Gold", featureSkus.ElementAt(2).SkuName);
            Assert.AreEqual("Feature 1", featureSkus.ElementAt(3).FeatureName);
            Assert.AreEqual("Silver", featureSkus.ElementAt(3).SkuName);
            Assert.AreEqual("Feature 2", featureSkus.ElementAt(4).FeatureName);
            Assert.AreEqual("Silver", featureSkus.ElementAt(4).SkuName);
            Assert.AreEqual("Feature 1", featureSkus.ElementAt(5).FeatureName);
            Assert.AreEqual("Free", featureSkus.ElementAt(5).SkuName);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldAssertFeatureIsEnabledForGoldUser()
    {
        var mockRoleProvider = new Mock<RoleProvider>();
        mockRoleProvider.Setup(r => r.GetRolesForUser("Gold User")).Returns(new[] { "Gold" });

        Freemium.Initialize("wishlistdbReadonly");
        Freemium.RoleProvider = mockRoleProvider.Object;

        Assert.IsTrue(Freemium.IsFeatureEnabledForUser("Claim Items", "Gold User"));
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldAssertFeatureIsNotEnabledForSilverUser()
    {
        var mockRoleProvider = new Mock<RoleProvider>();
        mockRoleProvider.Setup(r => r.GetRolesForUser("silver-user")).Returns(new[] { "silver" });

        Freemium.Initialize("wishlistdbReadonly");
        Freemium.RoleProvider = mockRoleProvider.Object;

        Assert.IsFalse(Freemium.IsFeatureEnabledForUser("create-lists", "silver-user"));
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldGetAllFeatures()
    {
        Freemium.Initialize("wishlistdbReadonly");

        IEnumerable<dynamic> result = Freemium.GetAllFeatures();

        Assert.IsNotNull(result);
        Assert.AreEqual(4, result.Count());
        Assert.IsTrue(result.Any(s => s.Name == "Claim Items"));
        Assert.IsTrue(result.Any(s => s.Name == "Share Lists"));
        Assert.IsTrue(result.Any(s => s.Name == "Sort Lists"));
        Assert.IsTrue(result.Any(s => s.Name == "Create Lists"));
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldGetAllStockKeepingUnits()
    {
        Freemium.Initialize("wishlistdbReadonly");

        IEnumerable<dynamic> result = Freemium.GetAllStockKeepingUnits();

        Assert.IsNotNull(result);
        Assert.AreEqual(3, result.Count());
        Assert.IsTrue(result.Any(s => s.Name == "Gold"));
        Assert.IsTrue(result.Any(s => s.Name == "Silver"));
        Assert.IsTrue(result.Any(s => s.Name == "Free"));
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldGetAllFeaturesForStockKeepingUnit()
    {
        Freemium.Initialize("wishlistdbReadonly");

        IEnumerable<dynamic> result = Freemium.GetAllFeaturesForStockKeepingUnit("Free");

        Assert.IsNotNull(result);
        Assert.AreEqual(1, result.Count());
        Assert.IsTrue(result.Any(s => s.Name == "Claim Items"));
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShoulGetAllStockKeepingUnitsForFeature()
    {
        Freemium.Initialize("wishlistdbReadonly");

        var result = Freemium.GetAllStockKeepingUnitsForFeature("Sort Lists");

        Assert.IsNotNull(result);
        Assert.AreEqual(1, result.Count());
        Assert.IsTrue(result.Any(s => s.Name == "Gold"));
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldGetFeature()
    {
        Freemium.Initialize("wishlistdbReadonly");
        var feature = Freemium.GetFeature("Claim Items");

        Assert.IsNotNull(feature);
        Assert.AreEqual("Claim Items", feature.Name);
        Assert.AreEqual("Allows users to claim items within a list", feature.Description);
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldCreateFeature()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Email Account";
        string description = "20 GB Webmail account.";

        Freemium.CreateFeature(name, description);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expected = db.QuerySingle("SELECT * FROM Freemium_Feature f WHERE f.Name = @0", name);

            Assert.IsNotNull(expected);
            Assert.AreEqual(expected.Description, description);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    [ExpectedException(typeof(SqlCeException))]
    public void ShouldTrhowExceptionWhenCreatingDuplicatedFeature()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Claim Items";
        string description = "Allows users to claim items within a list.";

        Freemium.CreateFeature(name, description);
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldRemoveFeature()
    {
        Freemium.Initialize("wishlistdbUpdatable");
        
        int featureId = 3;
        string featureName = "Sort Lists";

        Freemium.RemoveFeature(featureName);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            // Deleted from Features        
            var expected = db.QuerySingle("SELECT * FROM Freemium_Feature f where f.Name = @0", featureName);
            Assert.IsNull(expected);

            // Deleted from Features - Stock Keeping Unit
            expected = db.QuerySingle("SELECT * FROM Freemium_FeatureStockKeepingUnit fsku where fsku.FeatureId = @0", featureId);
            Assert.IsNull(expected);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldUpdateFeature()
    {
        Freemium.Initialize("wishlistdbUpdatable");
        
        string featureName = "Create Lists";

        Freemium.UpdateFeature(featureName, "Create Lists Updated");
        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expected = db.QuerySingle("SELECT * FROM Freemium_Feature f WHERE f.Name = @0", featureName);

            Assert.IsNotNull(expected);
            Assert.AreEqual(expected.Description, "Create Lists Updated");
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    [DeploymentItem(@"Resources\FeatureBySkuMatrixDefault.xml")]
    public void ShouldRenderFeatureBySkuMatrix()
    {
        Freemium.Initialize("wishlistdbReadonly");

        var result = Freemium.GetFeatureByStockKeepingUnitMatrixHtml();

        var expected = File.ReadAllText("FeatureBySkuMatrixDefault.xml");

        Assert.AreEqual(expected.RemoveBlanks(), result.ToString().RemoveBlanks());
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    [DeploymentItem(@"Resources\SkuList.xml")]
    public void ShouldRenderStockKeepingUnitsList()
    {
        Freemium.Initialize("wishlistdbReadonly");

        var result = Freemium.GetStockKeepingUnitsListHtml();

        var expected = File.ReadAllText("SkuList.xml");

        Assert.AreEqual(expected.RemoveBlanks(), result.ToString().RemoveBlanks());
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldCreateStockKeepingUnit()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "New Sku";
        string description = "This is a new Sku";
        decimal price = 0.50M;

        Freemium.CreateStockKeepingUnit(name, description, price);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expected = db.QuerySingle("SELECT * FROM Freemium_StockKeepingUnit sku where sku.Name = @0", name);

            Assert.IsNotNull(expected);
            Assert.AreEqual(expected.Description, description);
            Assert.AreEqual(expected.Price, price);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    [ExpectedException(typeof(SqlCeException))]
    public void ShouldTrhowExceptionWhenCreatingDuplicatedSku()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Silver";
        string description = "Silver User";
        decimal price = 50M;

        Freemium.CreateStockKeepingUnit(name, description, price);
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldRemoveStockKeepingUnit()
    {
        Freemium.Initialize("wishlistdbUpdatable");
        
        var skuName = "gold";
        var skuId = 1;

        Freemium.RemoveStockKeepingUnit(skuName);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            // Deleted from Stock Keeping Unit
            var expected = db.QuerySingle("SELECT * FROM Freemium_StockKeepingUnit sku WHERE sku.Name = @0", skuName);
            Assert.IsNull(expected);

            // Deleted from Features - Stock Keeping Unit        
            expected = db.QuerySingle("SELECT * FROM Freemium_FeatureStockKeepingUnit fsku WHERE fsku.StockKeepingUnitId = @0", skuId);
            Assert.IsNull(expected);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    public void ShouldGetStockKeepingUnit()
    {
        Freemium.Initialize("wishlistdbReadonly");

        var result = Freemium.GetStockKeepingUnit("Gold");
        Assert.IsNotNull(result);
        Assert.AreEqual(result.Description, "Gold Users");
        Assert.AreEqual(result.Price, 120M);
    }   

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldUpdateStockKeepingUnit()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        var skuName = "Silver";
        var skuDescription = "Silver Updated";
        var skuPrice = 90M;

        Freemium.UpdateStockKeepingUnit(skuName, skuDescription, skuPrice);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expected = db.QuerySingle("SELECT * FROM Freemium_StockKeepingUnit sku WHERE sku.Name = @0", skuName);
            Assert.IsNotNull(expected);
            Assert.AreEqual(skuDescription, expected.Description);
            Assert.AreEqual(skuPrice, expected.Price);
        }
    }

    [TestMethod]
    [DeploymentItem("Resources/Koala.jpg")]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldCreateStockKeepingUnitWithImage()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Sku With Image";
        string description = "This is a Sku for test";
        decimal price = 0.50M;

        var image = "Koala.jpg";
        var imageContent = File.ReadAllBytes(image);

        Freemium.CreateStockKeepingUnit(name, description, price, imageContent);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expectedSku = db.QuerySingle("SELECT Id FROM Freemium_StockKeepingUnit sku where sku.Name = @0", name);

            Assert.IsNotNull(expectedSku);

            var expectedImg = db.QuerySingle("SELECT Image FROM Freemium_StockKeepingUnitImage skui where skui.StockKeepingUnitId = @0", expectedSku.Id);

            Assert.IsNotNull(expectedImg);

            Assert.IsNotNull(expectedImg.Image);
        }
    }

    [TestMethod]
    [DeploymentItem("Resources/Koala.jpg")]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldCreateStockKeepingUnitWithoutImage()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Sku Without Image";
        string description = "this is a test";
        decimal price = 50M;

        Freemium.CreateStockKeepingUnit(name, description, price, null);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expectedSku = db.QuerySingle("SELECT Id FROM Freemium_StockKeepingUnit sku where sku.Name = @0", name);

            Assert.IsNotNull(expectedSku);

            var expectedImg = db.QuerySingle("SELECT Image FROM Freemium_StockKeepingUnitImage skui where skui.StockKeepingUnitId = @0", expectedSku.Id);

            Assert.IsNull(expectedImg);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldUpdateStockKeepingUnitRemovingImage()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Free";
        string description = "Free Users";
        decimal price = 0M;

        Freemium.UpdateStockKeepingUnit(name, description, price, true, null);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expectedSku = db.QuerySingle("SELECT Id FROM Freemium_StockKeepingUnit sku where sku.Name = @0", name);

            Assert.IsNotNull(expectedSku);

            var expectedImg = db.QuerySingle("SELECT Image FROM Freemium_StockKeepingUnitImage skui where skui.StockKeepingUnitId = @0", expectedSku.Id);

            Assert.IsNull(expectedImg);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    [DeploymentItem(@"Resources\Koala.jpg")]
    public void ShouldUpdateStockKeepingUnitReplacingImage()
    {
        Freemium.Initialize("wishlistdbUpdatable");

        string name = "Free";
        string description = "Free Users";
        decimal price = 0M;

        var image = "Koala.jpg";
        var imageContent = File.ReadAllBytes(image);

        Freemium.UpdateStockKeepingUnit(name, description, price, true, imageContent);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var expectedSku = db.QuerySingle("SELECT Id FROM Freemium_StockKeepingUnit sku WHERE sku.Name = @0", name);

            Assert.IsNotNull(expectedSku);

            var expectedImg = db.QuerySingle("SELECT Image FROM Freemium_StockKeepingUnitImage skui WHERE skui.StockKeepingUnitId = @0", expectedSku.Id);

            Assert.IsNotNull(expectedImg);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldEnableFeatureForSilverSku()
    {
        var featureName = "Create Lists";
        var skuName = "Silver";

        Freemium.Initialize("wishlistdbUpdatable");

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var result = db.QuerySingle("SELECT * FROM freemium_FeatureStockKeepingUnit fs, freemium_Feature f, freemium_StockKeepingUnit s WHERE fs.FeatureId = f.Id AND fs.StockKeepingUnitId = s.Id AND f.Name = @0 AND s.Name = @1", featureName, skuName);            
            
            Assert.IsNull(result);
        }

        Freemium.UpdateFeatureForStockKeepingUnit(featureName, skuName, true);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var result = db.QuerySingle("SELECT * FROM freemium_FeatureStockKeepingUnit fs, freemium_Feature f, freemium_StockKeepingUnit s WHERE fs.FeatureId = f.Id AND fs.StockKeepingUnitId = s.Id AND f.Name = @0 AND s.Name = @1", featureName, skuName);

            Assert.IsNotNull(result);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbUpdatable.sdf")]
    public void ShouldDisableFeatureForGoldSku()
    {
        var featureName = "Create Lists";
        var skuName = "Gold";

        Freemium.Initialize("wishlistdbUpdatable");

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var result = db.QuerySingle("SELECT * FROM freemium_FeatureStockKeepingUnit fs, freemium_Feature f, freemium_StockKeepingUnit s WHERE fs.FeatureId = f.Id AND fs.StockKeepingUnitId = s.Id AND f.Name = @0 AND s.Name = @1", featureName, skuName);

            Assert.IsNotNull(result);
        }

        Freemium.UpdateFeatureForStockKeepingUnit(featureName, skuName, false);

        using (Database db = Database.Open("wishlistdbUpdatable"))
        {
            var result = db.QuerySingle("SELECT * FROM freemium_FeatureStockKeepingUnit fs, freemium_Feature f, freemium_StockKeepingUnit s WHERE fs.FeatureId = f.Id AND fs.StockKeepingUnitId = s.Id AND f.Name = @0 AND s.Name = @1", featureName, skuName);

            Assert.IsNull(result);
        }
    }

    [TestMethod]
    [DeploymentItem(@"App_Data\wishlistdbReadonly.sdf")]
    [DeploymentItem(@"Resources\Free.png")]
    public void ShouldRetrieveSkuImage()
    {
        Freemium.Initialize("wishlistdbReadonly");

        var image = Freemium.GetStockKeepingUnitImage("Free");
        
        var expected = File.ReadAllBytes("Free.png");

        Assert.IsNotNull(image);
        Assert.AreEqual(expected.Count(), image.Count());
    }
}